<?php

/**
 * ShipmentApi
 * PHP version 8.3.
 *
 * @category Class
 *
 * @author   OpenAPI Generator team
 *
 * @see     https://openapi-generator.tech
 */

/**
 * Selling Partner API for Orders.
 *
 * Use the Orders Selling Partner API to programmatically retrieve order information. With this API, you can develop fast, flexible, and custom applications to manage order synchronization, perform order research, and create demand-based decision support tools.   _Note:_ For the JP, AU, and SG marketplaces, the Orders API supports orders from 2016 onward. For all other marketplaces, the Orders API supports orders for the last two years (orders older than this don't show up in the response).
 *
 * The version of the OpenAPI document: v0
 * Generated by: https://openapi-generator.tech
 * Generator version: 7.9.0
 */

/**
 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 * https://openapi-generator.tech
 * Do not edit the class manually.
 */

namespace SpApi\Api\orders\v0;

use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7\MultipartStream;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\RequestOptions;
use SpApi\ApiException;
use SpApi\AuthAndAuth\RestrictedDataTokenSigner;
use SpApi\Configuration;
use SpApi\HeaderSelector;
use SpApi\Model\orders\v0\UpdateShipmentStatusRequest;
use SpApi\ObjectSerializer;
use Symfony\Component\RateLimiter\LimiterInterface;
use Symfony\Component\RateLimiter\RateLimiterFactory;
use Symfony\Component\RateLimiter\Storage\InMemoryStorage;

/**
 * ShipmentApi Class Doc Comment.
 *
 * @category Class
 *
 * @author   OpenAPI Generator team
 *
 * @see     https://openapi-generator.tech
 */
class ShipmentApi
{
    public ?LimiterInterface $updateShipmentStatusRateLimiter;
    protected ClientInterface $client;

    protected Configuration $config;

    protected HeaderSelector $headerSelector;

    /**
     * @var int Host index
     */
    protected int $hostIndex;

    private bool $rateLimiterEnabled;
    private InMemoryStorage $rateLimitStorage;

    /**
     * @param int $hostIndex (Optional) host index to select the list of hosts if defined in the OpenAPI spec
     */
    public function __construct(
        Configuration $config,
        ?ClientInterface $client = null,
        ?bool $rateLimiterEnabled = true,
        ?HeaderSelector $selector = null,
        int $hostIndex = 0
    ) {
        $this->config = $config;
        $this->rateLimiterEnabled = $rateLimiterEnabled;

        if ($rateLimiterEnabled) {
            $this->rateLimitStorage = new InMemoryStorage();

            $factory = new RateLimiterFactory(Configuration::getRateLimitOptions('ShipmentApi-updateShipmentStatus'), $this->rateLimitStorage);
            $this->updateShipmentStatusRateLimiter = $factory->create('ShipmentApi-updateShipmentStatus');
        }

        $this->client = $client ?: new Client();
        $this->headerSelector = $selector ?: new HeaderSelector();
        $this->hostIndex = $hostIndex;
    }

    /**
     * Set the host index.
     *
     * @param int $hostIndex Host index (required)
     */
    public function setHostIndex(int $hostIndex): void
    {
        $this->hostIndex = $hostIndex;
    }

    /**
     * Get the host index.
     *
     * @return int Host index
     */
    public function getHostIndex(): int
    {
        return $this->hostIndex;
    }

    public function getConfig(): Configuration
    {
        return $this->config;
    }

    /**
     * Operation updateShipmentStatus.
     *
     * @param string                      $order_id
     *                                                         An Amazon-defined order identifier, in 3-7-7 format. (required)
     * @param UpdateShipmentStatusRequest $payload
     *                                                         The request body for the &#x60;updateShipmentStatus&#x60; operation. (required)
     * @param null|string                 $restrictedDataToken Restricted Data Token (RDT) for accessing restricted resources (optional, required for operations that return PII)
     *
     * @throws ApiException              on non-2xx response
     * @throws \InvalidArgumentException
     */
    public function updateShipmentStatus(
        string $order_id,
        UpdateShipmentStatusRequest $payload,
        ?string $restrictedDataToken = null
    ): void {
        $this->updateShipmentStatusWithHttpInfo($order_id, $payload, $restrictedDataToken);
    }

    /**
     * Operation updateShipmentStatusWithHttpInfo.
     *
     * @param string                      $order_id
     *                                                         An Amazon-defined order identifier, in 3-7-7 format. (required)
     * @param UpdateShipmentStatusRequest $payload
     *                                                         The request body for the &#x60;updateShipmentStatus&#x60; operation. (required)
     * @param null|string                 $restrictedDataToken Restricted Data Token (RDT) for accessing restricted resources (optional, required for operations that return PII)
     *
     * @return array of , HTTP status code, HTTP response headers (array of strings)
     *
     * @throws ApiException              on non-2xx response
     * @throws \InvalidArgumentException
     */
    public function updateShipmentStatusWithHttpInfo(
        string $order_id,
        UpdateShipmentStatusRequest $payload,
        ?string $restrictedDataToken = null
    ): array {
        $request = $this->updateShipmentStatusRequest($order_id, $payload);
        if (null !== $restrictedDataToken) {
            $request = RestrictedDataTokenSigner::sign($request, $restrictedDataToken, 'ShipmentApi-updateShipmentStatus');
        } else {
            $request = $this->config->sign($request);
        }

        try {
            $options = $this->createHttpClientOption();

            try {
                if ($this->rateLimiterEnabled) {
                    $this->updateShipmentStatusRateLimiter->consume()->ensureAccepted();
                }
                $response = $this->client->send($request, $options);
            } catch (RequestException $e) {
                throw new ApiException(
                    "[{$e->getCode()}] {$e->getResponse()->getBody()}",
                    (int) $e->getCode(),
                    $e->getResponse() ? $e->getResponse()->getHeaders() : null,
                    $e->getResponse() ? (string) $e->getResponse()->getBody() : null
                );
            } catch (ConnectException $e) {
                throw new ApiException(
                    "[{$e->getCode()}] {$e->getMessage()}",
                    (int) $e->getCode(),
                    null,
                    null
                );
            }

            $statusCode = $response->getStatusCode();

            if ($statusCode < 200 || $statusCode > 299) {
                throw new ApiException(
                    sprintf(
                        '[%d] Error connecting to the API (%s)',
                        $statusCode,
                        (string) $request->getUri()
                    ),
                    $statusCode,
                    $response->getHeaders(),
                    (string) $response->getBody()
                );
            }

            return [null, $statusCode, $response->getHeaders()];
        } catch (ApiException $e) {
            $data = ObjectSerializer::deserialize(
                $e->getResponseBody(),
                '\SpApi\Model\orders\v0\UpdateShipmentStatusErrorResponse',
                $e->getResponseHeaders()
            );
            $e->setResponseObject($data);

            throw $e;
        }
    }

    /**
     * Operation updateShipmentStatusAsync.
     *
     * @param string                      $order_id
     *                                              An Amazon-defined order identifier, in 3-7-7 format. (required)
     * @param UpdateShipmentStatusRequest $payload
     *                                              The request body for the &#x60;updateShipmentStatus&#x60; operation. (required)
     *
     * @throws \InvalidArgumentException
     */
    public function updateShipmentStatusAsync(
        string $order_id,
        UpdateShipmentStatusRequest $payload
    ): PromiseInterface {
        return $this->updateShipmentStatusAsyncWithHttpInfo($order_id, $payload)
            ->then(
                function ($response) {
                    return $response[0];
                }
            )
        ;
    }

    /**
     * Operation updateShipmentStatusAsyncWithHttpInfo.
     *
     * @param string                      $order_id
     *                                              An Amazon-defined order identifier, in 3-7-7 format. (required)
     * @param UpdateShipmentStatusRequest $payload
     *                                              The request body for the &#x60;updateShipmentStatus&#x60; operation. (required)
     *
     * @throws \InvalidArgumentException
     */
    public function updateShipmentStatusAsyncWithHttpInfo(
        string $order_id,
        UpdateShipmentStatusRequest $payload,
        ?string $restrictedDataToken = null
    ): PromiseInterface {
        $returnType = '';
        $request = $this->updateShipmentStatusRequest($order_id, $payload);
        if (null !== $restrictedDataToken) {
            $request = RestrictedDataTokenSigner::sign($request, $restrictedDataToken, 'ShipmentApi-updateShipmentStatus');
        } else {
            $request = $this->config->sign($request);
        }
        if ($this->rateLimiterEnabled) {
            $this->updateShipmentStatusRateLimiter->consume()->ensureAccepted();
        }

        return $this->client
            ->sendAsync($request, $this->createHttpClientOption())
            ->then(
                function ($response) {
                    return [null, $response->getStatusCode(), $response->getHeaders()];
                },
                function ($exception) {
                    $response = $exception->getResponse();
                    $statusCode = $response->getStatusCode();

                    throw new ApiException(
                        sprintf(
                            '[%d] Error connecting to the API (%s)',
                            $statusCode,
                            $exception->getRequest()->getUri()
                        ),
                        $statusCode,
                        $response->getHeaders(),
                        (string) $response->getBody()
                    );
                }
            )
        ;
    }

    /**
     * Create request for operation 'updateShipmentStatus'.
     *
     * @param string                      $order_id
     *                                              An Amazon-defined order identifier, in 3-7-7 format. (required)
     * @param UpdateShipmentStatusRequest $payload
     *                                              The request body for the &#x60;updateShipmentStatus&#x60; operation. (required)
     *
     * @throws \InvalidArgumentException
     */
    public function updateShipmentStatusRequest(
        string $order_id,
        UpdateShipmentStatusRequest $payload
    ): Request {
        // verify the required parameter 'order_id' is set
        if (null === $order_id || (is_array($order_id) && 0 === count($order_id))) {
            throw new \InvalidArgumentException(
                'Missing the required parameter $order_id when calling updateShipmentStatus'
            );
        }
        // verify the required parameter 'payload' is set
        if (null === $payload || (is_array($payload) && 0 === count($payload))) {
            throw new \InvalidArgumentException(
                'Missing the required parameter $payload when calling updateShipmentStatus'
            );
        }

        $resourcePath = '/orders/v0/orders/{orderId}/shipment';
        $formParams = [];
        $queryParams = [];
        $headerParams = [];
        $httpBody = '';
        $multipart = false;

        // path params
        if (null !== $order_id) {
            $resourcePath = str_replace(
                '{orderId}',
                ObjectSerializer::toPathValue($order_id),
                $resourcePath
            );
        }

        $headers = $this->headerSelector->selectHeaders(
            ['application/json'],
            'application/json',
            $multipart
        );

        // for model (json/xml)
        if (isset($payload)) {
            if ('application/json' === $headers['Content-Type']) {
                $httpBody = \GuzzleHttp\json_encode(ObjectSerializer::sanitizeForSerialization($payload));
            } else {
                $httpBody = $payload;
            }
        } elseif (count($formParams) > 0) {
            if ($multipart) {
                $multipartContents = [];
                foreach ($formParams as $formParamName => $formParamValue) {
                    $formParamValueItems = is_array($formParamValue) ? $formParamValue : [$formParamValue];
                    foreach ($formParamValueItems as $formParamValueItem) {
                        $multipartContents[] = [
                            'name' => $formParamName,
                            'contents' => $formParamValueItem,
                        ];
                    }
                }
                // for HTTP post (form)
                $httpBody = new MultipartStream($multipartContents);
            } elseif ('application/json' === $headers['Content-Type']) {
                $httpBody = \GuzzleHttp\json_encode($formParams);
            } else {
                // for HTTP post (form)
                $httpBody = ObjectSerializer::buildQuery($formParams, $this->config);
            }
        }

        $defaultHeaders = [];
        if ($this->config->getUserAgent()) {
            $defaultHeaders['User-Agent'] = $this->config->getUserAgent();
        }

        $headers = array_merge(
            $defaultHeaders,
            $headerParams,
            $headers
        );

        $query = ObjectSerializer::buildQuery($queryParams, $this->config);

        return new Request(
            'POST',
            $this->config->getHost().$resourcePath.($query ? "?{$query}" : ''),
            $headers,
            $httpBody
        );
    }

    /**
     * Create http client option.
     *
     * @return array of http client options
     *
     * @throws \RuntimeException on file opening failure
     */
    protected function createHttpClientOption(): array
    {
        $options = [];
        if ($this->config->getDebug()) {
            $options[RequestOptions::DEBUG] = fopen($this->config->getDebugFile(), 'a');
            if (!$options[RequestOptions::DEBUG]) {
                throw new \RuntimeException('Failed to open the debug file: '.$this->config->getDebugFile());
            }
        }

        return $options;
    }
}
